#ifndef GUI_H
#define GUI_H

#include "Vector2.h"
#include "Colors.h"
#include "Input.h"
#include "StaticHashMap.h"

namespace Cvekas {

/// Determines how GUI elements are rendered and keeps rendering state
class GUIStyle : public boost::noncopyable 
{
public:
	/// Constructor
	GUIStyle() {}
	/// Destructor
	virtual ~GUIStyle() {}

	/// Sets new mouse position
	void setMousePos(const Vector2& mouse_pos) { this->mouse_pos = mouse_pos; }

	/// Returns true if mouse is inside button
	virtual bool hitButton(const Vector2& pos, const std::string& text) = 0;
	/// Returns true if mouse is inside switch
	virtual bool hitSwitch(const Vector2& pos, const std::string& text) = 0;
	/// Returns -1 if mouse is outside slider and value between 0 and 1 otherwise
	virtual float hitSlider(const Vector2& pos) = 0;
	/// Returns true if mouse is inside selector. 
	/// Also Sets button to 1 if mouse is inside prev button, 2 if next, 0 otherwise.
	virtual bool hitSelector(const Vector2& pos, const std::vector<std::string>& selections, uint& button) = 0;

	/// Draws text label
	virtual void drawLabel(const Vector2& pos, const std::string& text) = 0;
	/// Draws button
	virtual void drawButton(const Vector2& pos, const std::string& text, bool mouse_over, bool pressed) = 0;
	/// Draws switch
	virtual void drawSwitch(const Vector2& pos, const std::string& text, bool mouse_over, bool state) = 0;
	/// Draws slider
	virtual void drawSlider(const Vector2& pos, bool mouse_over, float state) = 0;
	/// Draws selector
	virtual void drawSelector(const Vector2& pos, const std::vector<std::string>& selections, uint item, 
		bool mouse_over, bool mouse_over_prev, bool mouse_over_next, bool pressed) = 0;

	/// Does what is neccessary to finish drawing current frame
	virtual void endFrame() = 0;

protected:
	Vector2 mouse_pos;
};

typedef boost::shared_ptr<GUIStyle> GUIStylePtr;

/// Graphical user interface
class GUI : public boost::noncopyable
{
public:
	/// Enmeration of all widget types
	enum WidgetType
	{
		WIDGET_LABEL,
		WIDGET_BUTTON,
		WIDGET_SWITCH,
		WIDGET_SLIDER,
		WIDGET_SELECTOR
	};

	/// Constructor
	/// \param gui_style GUIStyle to use for drawing gui
	/// \param input Input information provider
	GUI(GUIStylePtr gui_style, InputPtr input);
	
	/// Destructor
	virtual ~GUI();

	/// Draws text label
	/// \param pos Coordinates of topleft corner of text
	/// \param text Text to draw
	void doLabel(const Vector2& pos, const std::string& text);
	
	/// Draws interactive button
	/// \param pos Coordinates of topleft corner of button
	/// \param text Text to draw in button
	/// \returns true if button was pressed on this frame
	bool doButton(const Vector2& pos, const std::string& text);
	
	/// Draws interactive switch (button with 2 states)
	/// \param pos Coordinates of topleft corner of switch
	/// \param text Text to draw in switch
	/// \returns true if switch is currently in 'on' state
	bool doSwitch(const Vector2& pos, const std::string& text);
	
	/// Draws interactive slider widget
	/// \param pos Coordinates of topleft corner of slider
	/// \returns Slider position in range [0.0, 1.0)
	float doSlider(const Vector2& pos);

	/// Draws interactive selector widget
	/// \param pos Coordinates of topleft corner of selector
	/// \param selections Selection values
	/// \returns Index of selection
	uint doSelector(const Vector2& pos, const std::vector<std::string>& selections);

	/// Returns state of switch at specified coordinates
	bool getSwitchState(const Vector2& pos);
	/// Returns state of slider at specified coordinates
	float getSliderState(const Vector2& pos);
	/// Returns state of selector at specified coordinates
	uint getSelectorState(const Vector2& pos);

	/// Modifies switch state
	void setSwitchState(const Vector2& pos, bool state);
	/// Modifies slider state
	void setSliderState(const Vector2& pos, float state);
	/// Modifies selector state
	void setSelectorState(const Vector2& pos, uint state);

	/// Must be called once per frame, invokes GUIStyle::endFrame()
	void present();

	/// Returns hash code for widget. For internal use.
	static uint hashWidget(WidgetType type, const Vector2& pos);

	/// Widgets hash map size. 
	/// Utilizing less than 75% of it is OK, so maximum of about 190 unique widgets can used.
	static const uint cache_size = 256;

private:
	union WidgetState
	{
		bool switch_state;
		float slider_state;
		uint selector_state;
	};

	void beginWidget();

	GUIStylePtr gui_style;
	InputPtr input;

	bool presented;
	StaticHashMap<WidgetState, uint, cache_size> cache;
};

typedef boost::shared_ptr<GUI> GUIPtr;

/// Converts arbitrary float to slider range [0.0, 1.0]
inline float toSliderRange(float a, float min, float max)
{
	return (a - min) / (max - min);
}
/// Converts float from slider range to arbitrary one
inline float fromSliderRange(float a, float min, float max)
{
	return min + a * (max - min);
}

} // namespace

#endif